<?php

namespace App\Kuafu\Shop\Service;

use AlibabaCloud\SDK\Facebody\V20191230\Facebody;
use AlibabaCloud\SDK\Facebody\V20191230\Models\AddFaceImageTemplateAdvanceRequest;
use AlibabaCloud\SDK\Facebody\V20191230\Models\MergeImageFaceAdvanceRequest;
use AlibabaCloud\Tea\Utils\Utils;
use AlibabaCloud\Tea\Utils\Utils\RuntimeOptions;
use App\Exceptions\BaseException;
use App\Kuafu\Banner\Enum\BannerEnum;
use App\Kuafu\Banner\Model\BannerCommonModel;
use App\Kuafu\Banner\Model\BannerCommonShopMappingsModel;
use App\Kuafu\Banner\Service\BannerCommonService;
use App\Kuafu\Base\Service\BaseService;
use App\Kuafu\Common\Service\CommonService;
use App\Kuafu\Common\Service\FileService;
use App\Kuafu\Config\Model\ConfigModel;
use App\Kuafu\Config\Service\ConfigService;
use App\Kuafu\CouponPackage\Enum\CouponPackageEnum;
use App\Kuafu\Mqtt\Service\MqttService;
use App\Kuafu\Order\Enum\CodeEnum as OrderModelCodeEnum;
use App\Kuafu\Order\Enum\OrderCodeEnum;
use App\Kuafu\Order\Exceptions\OrderException;
use App\Kuafu\Order\Model\OrderSplitModel;
use App\Kuafu\Order\Service\OrderMerchantBaseService;
use App\Kuafu\Product\Model\Product;
use App\Kuafu\Shop\Enum\DeviceCacheEnum;
use App\Kuafu\Shop\Enum\DeviceMerchantEnum;
use App\Kuafu\Shop\Enum\ShopCodeEnum;
use App\Kuafu\Shop\Exceptions\ShopException;
use App\Kuafu\Shop\Model\CameraModel;
use App\Kuafu\Shop\Model\Shop;
use App\Kuafu\Shop\Model\ShopMonitorDataCHModel;
use App\Kuafu\Shop\Model\ShopMonitorDataHourModel;
use App\Kuafu\Shop\Model\ShopMonitorDataModel;
use App\Kuafu\Shop\Model\ShopProductStatus;
use App\Kuafu\User\Service\AuthService;
use App\Kuafu\Wechat\Service\WechatMiniProgramService;
use Darabonba\OpenApi\Models\Config;
use Exception;
use GuzzleHttp\Psr7\Stream;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;

class DeviceService extends BaseService
{
    private array $showTimeTabInfo, $compareTimeTabInfo, $overviewCompareInfo;

    private int $showTimeTab;

    private int $compareTimeTab;


    /**
     * @return array|void
     * @throws BaseException
     */
    public static function config()
    {
        try {
            return [
                "sort" => enumToSelection(CameraModel::TV_POSITION_TEXT)
            ];
        } catch (\Exception $exception) {
            handleException($exception, "获取表单配置失败");
        }
    }

    public static function deviceMMCList(int $shopId)
    {
        $list = CameraModel::query()
                           ->where("deviceType", CameraModel::DEVICE_TYPE_MMC)
                           ->where("shopId", $shopId)
                           ->where("isDeleted", 0)
                           ->get();

        $result = [];

        $list->each(function ($v) use (&$result) {
            $result[] = [
                'id'              => $v->id,
                'serialNo'        => $v->serialNo,
                'name'            => $v->name,
                'createdAt'       => $v->createdAt->toDateTimeString(),
                'lastUpdateAt'    => empty((int)$v->productKey) ? '' : date('Y-m-d H:i:s', (int)$v->productKey),
                'lastTemperature' => empty($v->deviceName) ? '' : $v->deviceName . '℃'
            ];
        });

        return $result;
    }

    /**
     * @throws Exception
     */
    public static function shopTv(int $shopId)
    {
        try {
            $shopTvClosure = function () use ($shopId) {
                return CameraModel::query()
                                  ->where("deviceType", CameraModel::DEVICE_TYPE_TV)
                                  ->where("shopId", $shopId)
                                  ->where("isDeleted", 0)
                                  ->orderBy("sort", "asc")
                                  ->get()
                                  ->map(function ($v) {
                                      return [
                                          "id"       => $v->id,
                                          "serialNo" => $v->serialNo,
                                          "sort"     => (int)$v->sort,
                                          "name"     => $v->name,
                                      ];
                                  })->values()->toArray();
            };

            return [
                "shopTv"      => $shopTvClosure(),
                "rollingTime" => 10
            ];
        } catch (\Exception $exception) {
            handleException($exception, "获取门店电视设备失败");
        }
    }

    /**
     * @param object $params
     * @param int $userId
     * @param array $bannerIds
     * @return Builder|Model|void
     * @throws BaseException
     */
    public static function bind(object $params, int $userId = 0, array $bannerIds = [])
    {
        try {
            // 判断其他设备的叫号是否打开
            if (!empty($params->productKey) && $params->productKey) {
                CameraModel::query()
                           ->where('shopId', $params->shopId)
                           ->where('deviceType', CameraModel::DEVICE_TYPE_TV)
                           ->update(['productKey' => 0]);
            }

            $createCamera = CameraModel::query()->create((array)$params);

            // 商家自选广告图
            if ($params->deviceType == CameraModel::DEVICE_TYPE_TV) {
                self::bannerShopMappingInsert($params->shopId, $userId, $params->sort, $params->sort, $bannerIds);
            }

            // MQTT 推送设备更新配置
            (new MqttService(0, $params->shopId))->updateDeviceConfig();

            return $createCamera;

        } catch (\Exception $exception) {
            handleException($exception, "设备绑定失败");
        }
    }

    /**
     * 解除绑定
     *
     * @throws Exception
     */
    public static function rmBind(object $params)
    {
        try {
            CameraModel::query()->where("serialNo", $params->serialNo)->update(["isDeleted" => 1]);
            self::forgetGetDeviceRowKey($params->serialNo);

            // MQTT 推送设备更新配置
            (new MqttService(0, $params->shopId))->updateDeviceConfig();
        } catch (\Exception $exception) {
            handleException($exception, "解除绑定失败");
        }
    }

    /**
     * 设备广告
     *
     * @param string $serialNo
     * @param int $shopId
     * @param int $sort
     * @param int $type 默认0-查询设备广告 1-查询是否存在后台系统配置广告
     * @return array
     * @throws BaseException
     */
    public static function deviceAdv(string $serialNo, int $shopId = 0, int $sort = 0, $type = 0): array
    {
        try {
            $positionMap = [
                CameraModel::TV_POSITION_SORT_1 => BannerEnum::TYPE_TV_SORT_1,
                CameraModel::TV_POSITION_SORT_2 => BannerEnum::TYPE_TV_SORT_2,
                CameraModel::TV_POSITION_SORT_3 => BannerEnum::TYPE_TV_SORT_3,
                CameraModel::TV_POSITION_SORT_4 => BannerEnum::TYPE_TV_SORT_4,
                CameraModel::TV_POSITION_SORT_5 => BannerEnum::TYPE_TV_SORT_5,
                CameraModel::TV_POSITION_SORT_6 => BannerEnum::TYPE_TV_SORT_6,
            ];

            $deviceDetail = DeviceService::getDeviceRow($serialNo);

            $deviceAdvFunc = function () use ($type, $shopId, $sort, $positionMap, $serialNo, $deviceDetail) {
                if ($serialNo && !$shopId && !$sort) {
                    $shopId = $deviceDetail->shopId;
                    $sort   = $deviceDetail->sort;
                }
                $shopDetail = Shop::query()->where("id", $shopId)->first();
                $time       = time();
                $result     = self::getBannerSystemConfig($shopId, $shopDetail->type, $time, $sort, $positionMap);

                if ($type) {//1-查询是否存在后台系统配置广告
                    return $result;
                }

                // 查询商家是否有自选商屏
                if (!$result && isset($deviceDetail)) {
                    $result = self::getBannerOptionalData($shopId, $deviceDetail->toArray(), $shopDetail->menuId ?? 0);
                }

                if (empty($result)) {
                    return self::getBannerDefaultConfig($shopId, $shopDetail->menuId ?? 0, $sort,
                        $positionMap);
                }
                return $result;
            };

            return [
                "adv"         => $deviceAdvFunc(),
                'version'     => $deviceDetail->updatedAt->timestamp ?? 0,
                "rollingTime" => 10
            ];
        } catch (\Exception $exception) {
            handleException($exception, "获取设备广告信息失败");
        }
    }
//
//    /**
//     * 获取客流数据 需要手动 while 循环获取
//     *
//     * @param int $shopId
//     * @param int $startAt
//     * @param int $endAt
//     * @param int $page
//     * @return array|void
//     * @throws BaseException
//     */
//    public static function getPassengerFlow(int $shopId, int $startAt, int $endAt, int $page = 1)
//    {
//        try {
//            $klCameraInfo = Cache::remember('alyKfCameraInfo' . $shopId, 3600, function () use ($shopId) {
//                return CameraModel::query()->where('shopId', $shopId)
//                                  ->where('type', CodeEnum::SHOP_CAMERA_TYPE_1)
//                                  ->where('supplier', CameraModel::SUPPLIER_ALIYUN)
//                                  ->where('tripleType', CodeEnum::SHOP_CAMERA_TRIPLE_TYPE_1)
//                                  ->first();
//            });
//
//            if (empty($klCameraInfo)) {
//                return [];
//            }
//
//            $camera                   = new CameraService();
//            $camera->cameraSdkService = new AliYunCamera($klCameraInfo->productKey, $klCameraInfo->deviceName);
//            $streamData               = $camera->getPassengerFlowData($startAt, $endAt, $page);
//
//            return $streamData;
//
//        } catch (\Exception $exception) {
//            handleException($exception, "获取客流失败");
//        }
//    }

    /**
     * @throws Exception
     */
    public static function list(int $page, int $pageSize, int $shopId, string $status)
    {
        try {
            $devicePage = CameraModel::query()
                                     ->when($shopId > 0, function ($query) use ($shopId) {
                                         $query->where("shopId", $shopId);
                                     })
                                     ->when(strlen($status), function ($query) use ($status) {
                                         $query->where("status", $status);
                                     })
                                     ->where("deviceType", CameraModel::DEVICE_TYPE_TV)
                                     ->orderBy("id", "desc")
                                     ->paginate($pageSize, ['*'], "page", $page);

            return [
                "page"           => $page,
                "total"          => $devicePage->total(),
                "pageSize"       => $pageSize,
                "shopTelevision" => $devicePage->items()
            ];
        } catch (\Exception $exception) {
            handleException($exception, "获取设备列表失败");
        }
    }


    /**
     * @param $shopId
     * @param $data
     * @throws ShopException
     */
    public static function addMonitorData($shopId, $data)
    {

        try {
            $shopInfo = ShopService::getShopInfo($shopId);
            if (!$shopInfo) {
                throw new ShopException(ShopCodeEnum::ERR_SHOP_ID_NO_EXIST);
            }
            $time       = time();
            $user       = AuthService::getAuthUser();
            $insertData = [];
            foreach ($data as $v) {
                $baseData     = [
                    "shopId"       => $shopId,
                    "monitorId"    => $v['monitorId'],
                    "type"         => $v['type'],
                    "dataAt"       => $v['dataAt'],
                    "count"        => $v['count'],
                    "createdAt"    => $time,
                    "updatedAt"    => $time,
                    "createdByUid" => $user['id'],
                    "updatedByUid" => $user['id'],
                ];
                $insertData[] = $baseData;
            }

            collect($insertData)->chunk(500)->each(function ($item) {
                ShopMonitorDataModel::query()->insert($item->toArray());
            });
        } catch (\Exception $e) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_INSERT_MONITOR_DATA, $e->getMessage());
        }
    }

    /**
     * @param $shopId
     * @param $data
     * @throws ShopException
     */
    public static function addTemperatureData($shopId, $data)
    {

        try {
            $shopInfo = ShopService::getShopInfo($shopId);
            if (!$shopInfo) {
                throw new ShopException(ShopCodeEnum::ERR_SHOP_ID_NO_EXIST);
            }
            $time        = time();
            $user        = AuthService::getAuthUser();
            $insertData  = [];
            $monitorData = [];
            foreach ($data as $v) {
                $baseData                     = [
                    "shopId"       => $shopId,
                    "monitorId"    => $v['monitorId'],
                    "type"         => 0,
                    "dataAt"       => $v['dataAt'],
                    "temperature"  => $v['temperature'],
                    "createdAt"    => $time,
                    "updatedAt"    => $time,
                    "createdByUid" => $user['id'],
                    "updatedByUid" => $user['id'],
                ];
                $insertData[]                 = $baseData;
                $monitorData[$v['monitorId']] = [
                    "id"          => $v['monitorId'],
                    "dataAt"      => $v['dataAt'],
                    "temperature" => $v['temperature'],
                ];
            }
            collect($insertData)->chunk(500)->each(function ($item) {
                ShopMonitorDataModel::query()->insert($item->toArray());
            });
            //更新秒秒测温度最新数据
            foreach ($monitorData as $v) {
                CameraModel::query()->where('id', $v['id'])->update([
                    'productKey' => $v['dataAt'],
                    'deviceName' => $v['temperature']
                ]);
            }
        } catch (\Exception $e) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_INSERT_MONITOR_DATA, $e->getMessage());
        }
    }

    /**
     * @param $shopId
     * @param $data
     * @return array
     */
    public static function getTemperatureData($shopId, $data)
    {
        $selectColumns = [
            'id',
            'shopId',
            'monitorId',
            'dataAt',
            'temperature',
        ];

        $basePaginate = ShopMonitorDataCHModel::query()
                                              ->select($selectColumns)
                                              ->where('shopId', $shopId)
                                              ->where('monitorId', $data['monitorId'])
                                              ->whereBetween('dataAt',
                                                  [strtotime($data['startTime']), strtotime($data['endTime']) + 86400])
                                              ->orderBy("dataAt", "desc");
        $shopPaginate = $basePaginate->simplePaginate($data['pageSize'], $selectColumns, "page",
            $data['pageNum']);

        $getMonitorData = $shopPaginate->items();

        foreach ($getMonitorData as &$v) {
            $v['dataAt'] = date('Y-m-d H:i:s', $v['dataAt']);
        }
        return [
            "total" => $basePaginate->count(),
            "list"  => $getMonitorData,
        ];
    }

    /**
     * 设置查看的时间选项卡
     * @param int $showTimeTab
     * @param int $showStartAt
     * @param int $showEndAt
     * @return $this
     * @throws OrderException
     */
    public function setShowTimeTab(int $showTimeTab, int $showStartAt, int $showEndAt)
    {
        $this->showTimeTab = $showTimeTab;

        $timeObj                   = DeviceMerchantEnum::getIns()
                                                       ->setShowTimeTab($showTimeTab, $showStartAt, $showEndAt);
        $this->overviewCompareInfo = $timeObj->getOrderOverviewCompareInfo();
        $this->showTimeTabInfo     = $timeObj->getShowTimeTab();

        if ($showTimeTab == DeviceMerchantEnum::SHOW_TIME_TAB_DEFINE) {
            if ($showEndAt - $showStartAt > 86400 * 31) {
                throw new OrderException(OrderCodeEnum::ERR_SHOW_TIME_DURATION);
            }
        }

        return $this;
    }

    /**
     * 设置对比的时间选项卡
     * @param int $compareTimeTab
     * @param int $compareStartAt
     * @param int $compareEndAt
     * @return $this
     * @throws OrderException
     */
    public function setCompareTimeTab(int $compareTimeTab, int $compareStartAt, int $compareEndAt)
    {
        $this->compareTimeTab     = $compareTimeTab;
        $this->compareTimeTabInfo = DeviceMerchantEnum::getIns()
                                                      ->setCompareTimeTab($compareTimeTab, $compareStartAt,
                                                          $compareEndAt)
                                                      ->getCompareTimeTab();
        return $this;
    }


    /**
     * 监控设备 - 折线图
     * @param $shopId
     * @param $brandId
     * @param int $statisticsType
     * @return array
     */
    public function getMonitorData($shopId, $brandId, int $statisticsType): array
    {
        $timeList = array_merge([$this->showTimeTabInfo], [$this->compareTimeTabInfo]);

        $interval = DeviceMerchantEnum::getChartInterval($this->showTimeTab, $this->showTimeTabInfo[1],
            $this->showTimeTabInfo[2]);

        $data = $total = [];
        foreach ($timeList as $k => $time) {
            if ($k == 0) {
                // 当前的 - 取同时段对比
                $endTime = $time[2];
            } else {
                // 对比的 - 取完整的时间
                $endTime = $time[3];
            }
            $lists = $this->chartsBase($shopId, $brandId, $time[1], $endTime, $time[3], $interval, $statisticsType,
                $this->showTimeTab);

            $data['list'][] = [
                'name'           => DeviceMerchantEnum::STAT_TEXT[$statisticsType],
                'text'           => $time[0],
                'statisticsType' => $statisticsType,
                'numType'        => DeviceMerchantEnum::STAT_TO_NUM[$statisticsType] ?? DeviceMerchantEnum::NUM_TYPE_NUM,
                'lists'          => $lists['list'],
            ];
            $total[]        = $lists['data'];
        }
        //与上一个时间段对比
        $changeNumTotal     = $total[0]['total'] - $total[1]['total'];
        $changeNumIn        = $total[0]['in'] - $total[1]['in'];
        $changeNumPass      = $total[0]['pass'] - $total[1]['pass'];
        $changeNumRate      = OrderMerchantBaseService::formatPercent($total[0]['rate'] - $total[1]['rate'], 100);
        $changeNumOrder     = $total[0]['orderData'] - $total[1]['orderData'];
        $changeNumOrderRate = OrderMerchantBaseService::formatPercent($total[0]['orderRate'] - $total[1]['orderRate'],
            100);
        $data['data']       = [
            'timeInfo'  => [
                'showStartAt'    => $this->showTimeTabInfo[1],
                'showEndAt'      => $this->showTimeTabInfo[2],
                'compareStartAt' => $this->compareTimeTabInfo[1],
                'compareEndAt'   => $this->compareTimeTabInfo[2],
            ],
            'total'     => [
                'total'      => $total[0]['total'],
                'changeNum'  => $changeNumTotal,
                'changeRate' => OrderMerchantBaseService::formatPercent($changeNumTotal,
                    $total[0]['total'] - $changeNumTotal),
            ],
            'in'        => [
                'total'      => $total[0]['in'],
                'changeNum'  => $changeNumIn,
                'changeRate' => OrderMerchantBaseService::formatPercent($changeNumIn, $total[0]['in'] - $changeNumIn),
            ],
            'pass'      => [
                'total'      => $total[0]['pass'],
                'changeNum'  => $changeNumPass,
                'changeRate' => OrderMerchantBaseService::formatPercent($changeNumPass,
                    $total[0]['pass'] - $changeNumPass),
            ],
            'rate'      => [
                'total'      => $total[0]['rate'],
                'changeNum'  => $changeNumRate,
                'changeRate' => $changeNumRate,
            ],
            'orderRate' => [
                'total'      => $total[0]['orderRate'],
                'changeNum'  => $changeNumOrder,
                'changeRate' => $changeNumOrderRate,
            ],
        ];
        return $data;
    }

    // 底层的折线图实现
    protected function chartsBase(
        int $shopId,
        int $brandId,
        int $startTime,
        int $endTime,
        int $oriEndTime,
        int $interval,
        int $statisticsType,
        int $showTimeTab
    ) {
//        //经过数据
//        $getPassData = ShopMonitorDataCHModel::query()
//                                             ->select(raw("count as num, dataAt as time"))
//                                             ->where('shopId', $shopId)
//                                             ->where('type', ShopMonitorDataModel::TYPE_PASS_SHOP)
//                                             ->whereBetween('dataAt', [$startTime, $endTime])
//                                             ->orderBy("dataAt", "desc")
//                                             ->get()
//                                             ->toArray();
//
//        //进入数据
//        $getInData = ShopMonitorDataCHModel::query()
//                                           ->select(raw("count as num, dataAt as time"))
//                                           ->where('shopId', $shopId)
//                                           ->where('type', ShopMonitorDataModel::TYPE_IN_SHOP)
//                                           ->whereBetween('dataAt', [$startTime, $endTime])
//                                           ->orderBy("dataAt", "desc")
//                                           ->get()
//                                           ->toArray();

        //经过数据
        $shopMonitor = ShopMonitorDataHourModel::class;
//        $shopMonitor = ShopMonitorDataModel::class;
        $getPassData = $shopMonitor::query()
                                   ->selectRaw("count as num, dataAt as time")
                                   ->where([
                                       'shopId' => $shopId,
                                       'type'   => ShopMonitorDataModel::TYPE_PASS_SHOP
                                   ])
                                   ->whereBetween('dataAt', [$startTime, $endTime])
                                   ->orderBy("dataAt", "desc")
                                   ->get()
                                   ->toArray();

        //进入数据
        $getInData = $shopMonitor::query()
                                 ->selectRaw("count as num, dataAt as time")
                                 ->where('shopId', $shopId)
                                 ->where('type', ShopMonitorDataModel::TYPE_IN_SHOP)
                                 ->whereBetween('dataAt', [$startTime, $endTime])
                                 ->orderBy("dataAt", "desc")
                                 ->get()
                                 ->toArray();

        //堂食订单量
        $orderSplitData = OrderSplitModel::suffix($shopId)
                                         ->query()
                                         ->selectRaw("completeTime as time")
                                         ->where('shopId', $shopId)
                                         ->where('brandId', $brandId)
                                         ->where('orderStatus', OrderModelCodeEnum::ORDER_STATUS_FINISHED)
                                         ->whereBetween('completeTime', [$startTime, $endTime])
                                         ->get()
                                         ->toArray();

        $charts    = [];
        $nowTime   = date('H');
        $totalData = $inData = $passData = $orderData = 0;
        // 循环所有明细数据，并放到对应的时间段内
        for ($i = $startTime; $i < $endTime; $i += $interval) {
            $total = $inNum = $passNum = $num = $orderNum = $flagDel = 0;
            if ($showTimeTab == DeviceMerchantEnum::SHOW_TIME_TAB_TODAY && date('H',
                    $i) > $nowTime && $interval == 3600) {//今日分析-不能超过当前时间-小时
                $flagDel = 1;//存在则此时间不显示对应数量
            }
            foreach ($getInData as $v) {
                // 符合的情况
                if ($v['time'] >= $i && $v['time'] < $i + $interval && $flagDel == 0) {
                    $inNum += (int)$v['num'];
                    $total += (int)$v['num'];
                }
            }
            foreach ($getPassData as $v) {
                // 符合的情况
                if ($v['time'] >= $i && $v['time'] < $i + $interval && $flagDel == 0) {
                    $passNum += (int)$v['num'];
                    $total   += (int)$v['num'];
                }
            }
            foreach ($orderSplitData as $v) {
                // 符合的情况
                if ($v['time'] >= $i && $v['time'] < $i + $interval && $flagDel == 0) {
                    $orderNum++;
                }
            }
            switch ($statisticsType) {
                case DeviceMerchantEnum::TYPE_TOTAL ://客流 经过+进入
                    $num = $total;
                    break;
                case DeviceMerchantEnum::TYPE_PASS ://经过
                    $num = $passNum;
                    break;
                case DeviceMerchantEnum::TYPE_IN ://进入
                    $num = $inNum;
                    break;
                case DeviceMerchantEnum::TYPE_RATE ://进店转化率 进店客流/总客流
                    $num = OrderMerchantBaseService::formatPercent($inNum, $total);
                    break;
                case DeviceMerchantEnum::TYPE_ORDER_RATE ://下单转化率 堂食订单量/进店客流
                    $num = $inNum > 0 ? OrderMerchantBaseService::formatPercent($orderNum, $inNum) : 0;
                    break;
            }
            switch ($showTimeTab) {
                case DeviceMerchantEnum::SHOW_TIME_TAB_TODAY:
                case DeviceMerchantEnum::SHOW_TIME_TAB_YESTERDAY:
                    $time = sprintf("%02s", date('H', $i)) . ':00';
                    break;
                case DeviceMerchantEnum::SHOW_TIME_TAB_WEEK:
                    $w    = date('w', $i);
                    $time = CommonService::wToWord($w);
                    break;
                case DeviceMerchantEnum::SHOW_TIME_TAB_DEFINE:
                    if ($interval == 3600) {
                        $time = sprintf("%02s", date('H', $i)) . ':00';
                    } else {
                        $time = date('m/d', $i);
                    }
                    break;

                default:
                    $time = date('m/d', $i);
                    break;
            }
            $charts[]  = [
                'time' => $time,
                'num'  => $num,
            ];
            $totalData += $total;
            $inData    += $inNum;
            $passData  += $passNum;
            $orderData += $orderNum;
        }
        $data = [                                                                                                                                                                                                                                                                     //总数据
            'total'     => $totalData,
            //客流
            'in'        => $inData,
            //进入
            'pass'      => $passData,
            //订单数据
            'rate'      => OrderMerchantBaseService::formatPercent($inData, $totalData),
            //经过
            'orderData' => $orderData,
            //进店转化率 进店客流/总客流
            'orderRate' => $inData > 0 ? OrderMerchantBaseService::formatPercent($orderData, $inData) : 0,
            //下单转化率 堂食订单量/进店客流
        ];

        return ['list' => $charts, 'data' => $data];
    }

    /**
     * 设备详情
     * @param int $shopId
     * @param int $id
     * @return array
     */
    public static function detail(int $shopId, int $id): array
    {
        $cameraInfo = CameraModel::query()
                                 ->where("shopId", $shopId)
                                 ->where('id', $id)
                                 ->select([
                                     'id',
                                     'name',
                                     'serialNo',
                                     'queueSwitch',
                                     'soundSwitch',
                                     'sort',
                                     'bannerType',
                                     'productKey',
                                     'deviceName'
                                 ])
                                 ->first();
        if (!$cameraInfo) {
            return [];
        }
        $shopInfo    = ShopService::getShopInfo($shopId);
        $shopMenuId  = $shopInfo['menuId'] ?? 0;
        $bannerIds   = BannerCommonShopMappingsModel::query()
                                                    ->where('shopId', $shopId)
                                                    ->where('type', $cameraInfo->sort)
                                                    ->pluck('bannerId')->unique()->toArray();
        $combineData = $defaultData = [];
        if ($bannerIds) {
            BannerCommonModel::query()
                             ->whereIn('id', $bannerIds)
                             ->whereIn("type", array_keys(BannerEnum::TYPE_TV_MATERIAL_GROUP))
                             ->where('status', BannerEnum::STATUS_ON)
                             ->chunkById(10, function ($bannerCommonModel) use ($shopMenuId, $shopId, &$combineData) {
                                 foreach ($bannerCommonModel as $v) {
                                     switch ($v->type) {
                                         case BannerEnum::TYPE_TV_MATERIAL_1://商屏素材—组合 要先查询对应的单品素材id
                                             $imgs      = json_decode($v->imgs, true);
                                             $childIds  = array_column($imgs, 'id');
                                             $childList = [];
                                             if ($childIds) {
                                                 $childList = self::getBannerProductById($shopId, $childIds);
                                             }
                                             $combineData[] = [
                                                 'id'        => $v->id,
                                                 'name'      => $v->name,
                                                 'childIds'  => $childIds,
                                                 'childList' => $childList,
                                             ];
                                             break;
                                         case BannerEnum::TYPE_TV_MATERIAL_2://商屏素材—单品
                                             $bannerData = BannerCommonService::getBannerImgsByMaterial($shopId,
                                                 [$v->productId], $shopMenuId, [$v->id]);
                                             if ($bannerData) {
                                                 $combineData = array_merge($combineData, $bannerData);
                                             }
                                             break;
                                     }

                                 }
                             });
        }

        if (!$combineData) {//获取默认素材
            $defaultData = self::getBannerDefaultConfig($shopId, $shopDetail->menuId ?? 0, $cameraInfo->sort);
        }

        $cameraInfo->popPosition   = (int)$cameraInfo->deviceName;
        $cameraInfo->isQueueDevice = (int)$cameraInfo->productKey;
        return [
            'combineData' => $combineData,
            'defaultData' => $defaultData,
            'info'        => $cameraInfo->toArray(),
        ];
    }

    /**
     * @param int $shopId
     * @param int $type
     * @return array
     */
    public static function bannerSearch(int $shopId, int $type): array
    {
        switch ($type) {
            case BannerEnum::TYPE_TV_MATERIAL_1://商屏素材—组合 要先查询对应的单品素材id
                $combineData = [];
                BannerCommonModel::query()
                                 ->where('type', BannerEnum::TYPE_TV_MATERIAL_1)
                                 ->where('status', BannerEnum::STATUS_ON)
                                 ->chunkById(100,
                                     function ($bannerCommonModel) use (&$combineData) {
                                         foreach ($bannerCommonModel as $v) {
                                             $imgs = json_decode($v->imgs, true);
                                             foreach ($imgs as $img) {
                                                 if (isset($img['status']) && $img['status'] == BannerEnum::STATUS_ON) {
                                                     $combineData[] = [
                                                         'id'     => $v->id,
                                                         'name'   => $v->name,
                                                         'imgUrl' => $img['img'] ?? "",
                                                     ];
                                                     break;
                                                 }
                                             }
                                         }
                                     });
                return $combineData;
            case BannerEnum::TYPE_TV_MATERIAL_2://商屏素材—单品
                $productIds = BannerCommonModel::query()
                                               ->where('type', BannerEnum::TYPE_TV_MATERIAL_2)
                                               ->where('status', BannerEnum::STATUS_ON)
                                               ->get()->pluck('productId')->unique()->toArray();
                if (!$productIds) {
                    return [];
                }
                return BannerCommonService::getBannerImgsByMaterial($shopId, $productIds);
            default:
                return [];
        }
    }

    /**
     * 设备编辑
     * @param $request
     * @param int $id
     * @param int $shopId
     * @param int $userId
     * @param int $bannerType
     * @param array $bannerIds
     * @return array
     * @throws BaseException
     */
    public static function update($request, int $id, int $shopId, int $userId, int $bannerType, array $bannerIds = [])
    {
        try {
            $cameraInfo = CameraModel::query()
                                     ->where("shopId", $shopId)
                                     ->where('id', $id)
                                     ->first();
            if (!$cameraInfo) {
                return [];
            }
            CameraModel::query()->where('id', $id)->update([
                "serialNo"     => $request->serialNo,
                "name"         => CameraModel::TV_POSITION_TEXT[$request->sort],
                "sort"         => $request->sort,
                "queueSwitch"  => $request->queueSwitch ?? 0,
                "soundSwitch"  => $request->soundSwitch ?? 0,
                "bannerType"   => $bannerType,
                "updatedByUid" => $userId,
                "deviceName"   => $request->popPosition ?? 2,
                "productKey"   => $request->isQueueDevice ?? 0
            ]);
            // 判断其他设备的叫号是否打开
            if ($request->isQueueDevice) {
                CameraModel::query()
                           ->where('shopId', $shopId)
                           ->where('id', '!=', $id)
                           ->where('deviceType', CameraModel::DEVICE_TYPE_TV)
                           ->get()
                           ->each(function ($camera) {
                               self::forgetGetDeviceRowKey($camera->serialNo);
                               $camera->update(['productKey' => 0]);
                           });
            }

            // 商家自选广告图
            if ($cameraInfo->deviceType == CameraModel::DEVICE_TYPE_TV) {
                self::bannerShopMappingInsert($cameraInfo->shopId, $userId, $cameraInfo->sort, $request->sort,
                    $bannerIds);
            }

            self::forgetGetDeviceRowKey($cameraInfo->serialNo);

            // MQTT 推送设备更新配置
            (new MqttService(0, $shopId))->updateDeviceConfig();

        } catch (\Exception $exception) {
            handleException($exception, "设备编辑失败");
        }
    }

    /**
     * 商家自选广告图操作
     * @param int $shopId
     * @param int $userId
     * @param int $oldSort
     * @param int $newSort
     * @param array $bannerIds
     */
    public static function bannerShopMappingInsert(
        int $shopId,
        int $userId,
        int $oldSort,
        int $newSort,
        array $bannerIds
    ) {
        BannerCommonShopMappingsModel::query()->where('shopId', $shopId)
                                     ->where('type', $oldSort)
                                     ->update(['isDeleted' => 1]);
        if ($bannerIds) {
            $bannerShopMappingInsert = [];
            foreach ($bannerIds as $v) {
                $bannerShopMappingInsert[] = [
                    'shopId'       => $shopId,
                    'bannerId'     => $v,
                    'createdAt'    => time(),
                    'type'         => $newSort,
                    'createdByUid' => $userId
                ];
            }
            BannerCommonShopMappingsModel::query()->insert($bannerShopMappingInsert);
        }
    }

    /**
     * 获取商家自选商屏广告图
     * @param int $shopId
     * @param array $deviceDetail
     * @param int $shopMenuId
     * @return array
     */
    public static function getBannerOptionalData(
        int $shopId,
        array $deviceDetail,
        int $shopMenuId
    ): array {
        $bannerIds = BannerCommonShopMappingsModel::query()
                                                  ->where('shopId', $shopId)
                                                  ->where('type', $deviceDetail['sort'])
                                                  ->pluck('bannerId')->unique()->toArray();
        if (!$bannerIds) {
            return [];
        }

        $result = [];
        BannerCommonModel::query()
                         ->whereIn("id", $bannerIds)
                         ->whereIn("type", array_keys(BannerEnum::TYPE_TV_MATERIAL_GROUP))
                         ->where("status", BannerEnum::STATUS_ON)
                         ->orderBy('sort')
                         ->orderBy('id', 'desc')
                         ->get()->map(function ($v) use (&$result, $shopMenuId, $shopId) {
                $imgs = json_decode($v->imgs, true);
                switch ($v->type) {
                    case BannerEnum::TYPE_TV_MATERIAL_1://商屏素材—组合 要先查询对应的单品素材id
                        $childIds = array_column($imgs, 'id');
                        if ($childIds) {
                            foreach ($childIds as $childId) {
                                $productId  = BannerCommonModel::query()
                                                               ->where("id", $childId)
                                                               ->where("type", BannerEnum::TYPE_TV_MATERIAL_2)
                                                               ->where("status", BannerEnum::STATUS_ON)
                                                               ->value('productId') ?? 0;
                                $bannerData = BannerCommonService::getBannerImgsByMaterial($shopId,
                                    [$productId], $shopMenuId, [$childId]);
                                if ($bannerData) {
                                    $result = array_merge($result, $bannerData);
                                }
                            }
                        }
                        break;
                    case BannerEnum::TYPE_TV_MATERIAL_2://商屏素材—单品
                        $bannerData = BannerCommonService::getBannerImgsByMaterial($shopId,
                            [$v->productId], $shopMenuId, [$v->id]);
                        if ($bannerData) {
                            $result = array_merge($result, $bannerData);
                        }
                        break;
                }
            })->toArray();
        return $result;
    }

    /**
     * 广告位置查询是否系统配置了图片
     * @param $request
     * @param int $shopId
     * @return array
     * @throws Exception
     */
    public static function bannerConfig($request, int $shopId): array
    {
        $systemData  = self::deviceAdv('', $shopId, $request->sort, 1)['adv'];//设备广告
        $defaultData = [];
        if (!$systemData) {//获取默认素材
            $defaultData = self::getBannerDefaultConfig($shopId, 0, $request->sort);
        } else {
            foreach ($systemData as &$v) {
                $v['startAt'] = $v['startAt'] ? date('Y-m-d', $v['startAt']) : 0;
                $v['endAt']   = $v['endAt'] ? date('Y-m-d', $v['endAt']) : 0;
            }
        }
        return [
            'systemData'  => $systemData,
            'defaultData' => $defaultData,
        ];
    }


    /**
     * 获取后台系统配置素材
     * @param int $shopId
     * @param int $shopType
     * @param int $time
     * @param int $sort
     * @param array $positionMap
     * @return array
     */
    public static function getBannerSystemConfig(
        int $shopId,
        int $shopType,
        int $time,
        int $sort,
        array $positionMap
    ): array {
        $result = [];
        BannerCommonModel::query()
                         ->where("type", $positionMap[$sort])
                         ->where("status", 0)
                         ->where(function ($query) use ($time) {
                             $query->where('effectiveType', BannerEnum::EFFECTIVE_TYPE_ALWAYS)
                                   ->orWhere(function ($query) use ($time) {
                                       $query->where('effectiveType', BannerEnum::EFFECTIVE_TYPE_TIME_RANGE)
                                             ->where('startAt', '<', $time)
                                             ->where('endAt', '>', $time);
                                   });
                         })
                         ->where(function (
                             $query
                         ) use (
                             $shopId,
                             $shopType
                         ) {
                             $query->where(
                                 function ($query) use ($shopId) {
                                     $query->where("shopType",
                                         CouponPackageEnum::SHOP_TYPE_SELECTION)
                                           ->whereHas('bannerShopMappingRelate',
                                               function ($query) use ($shopId) {
                                                   $query->where('shopId', $shopId);
                                               }
                                           );
                                 }
                             )->orWhere(
                                 'shopType', '=', CouponPackageEnum::SHOP_TYPE_ALL
                             )->orWhere(
                                 'shopType', '=', $shopType
                             );
                         })
                         ->orderBy('sort')
                         ->orderBy('id', 'desc')
                         ->get()
                         ->map(function ($v) use (&$result) {
                             if ($v->imgs) {// 多图json数据
                                 $imgs = json_decode($v->imgs, true);
                                 if (!($imgs && is_array($imgs))) {
                                     return;
                                 }
                                 foreach ($imgs as $img) {
                                     $result[] = [
                                         "id"            => $v->id,
                                         "name"          => $v->name,
                                         "resourceType"  => $v->resourceType,
                                         "url"           => $img,
                                         "type"          => $v->type,
                                         "typeText"      => BannerEnum::TYPE_MAP[$v->type],
                                         "effectiveType" => $v->effectiveType,
                                         "startAt"       => $v->startAt,
                                         "endAt"         => $v->endAt,
                                     ];
                                 }
                                 return;
                             }
                             $result[] = [
                                 "id"            => $v->id,
                                 "name"          => $v->name,
                                 "resourceType"  => $v->resourceType,
                                 "url"           => picturePath($v->img),
                                 "type"          => $v->type,
                                 "typeText"      => BannerEnum::TYPE_MAP[$v->type],
                                 "effectiveType" => $v->effectiveType,
                                 "startAt"       => $v->startAt,
                                 "endAt"         => $v->endAt,
                             ];
                         })->toArray();
        return $result;
    }

    /**
     * 获取默认素材
     * @param int $shopId
     * @param int $shopMenuId
     * @param int $sort
     * @param array $positionMap
     * @return array
     */
    public static function getBannerDefaultConfig(
        int $shopId,
        int $shopMenuId,
        int $sort,
        array $positionMap = []
    ): array {
        if (!$positionMap) {
            $positionMap = [
                CameraModel::TV_POSITION_SORT_1 => BannerEnum::TYPE_TV_SORT_1,
                CameraModel::TV_POSITION_SORT_2 => BannerEnum::TYPE_TV_SORT_2,
                CameraModel::TV_POSITION_SORT_3 => BannerEnum::TYPE_TV_SORT_3,
                CameraModel::TV_POSITION_SORT_4 => BannerEnum::TYPE_TV_SORT_4,
                CameraModel::TV_POSITION_SORT_5 => BannerEnum::TYPE_TV_SORT_5,
                CameraModel::TV_POSITION_SORT_6 => BannerEnum::TYPE_TV_SORT_6,
            ];
        }
        if (!$shopMenuId) {
            $shopInfo   = ShopService::getShopInfo($shopId);
            $shopMenuId = $shopInfo['menuId'] ?? 0;
        }
        $menuDefaultBanner = json_decode(
            ConfigModel::getConfigByTypeKey('menuDefaultBanner', 'menuDefaultBanner')[0]["value"],
            true
        );

        $stinkyTofuProductId           = ConfigModel::getConfigByTypeKey('stinkyTofu',
            'stinkyTofu')[0]["value"];
        $stinkyTofuProductDetail       = Product::query()
                                                ->where("menuId", $shopMenuId)
                                                ->where("status", 0)
                                                ->where("id", $stinkyTofuProductId)
                                                ->first();
        $stinkyTofuProductStatusDetail = ShopProductStatus::query()
                                                          ->where("shopId", $shopId)
                                                          ->where("productId", $stinkyTofuProductId)
                                                          ->first();

        $menuDefaultBannerCollection = collect($menuDefaultBanner);
        if ($menuDefaultBannerCollection->where("id", $shopMenuId)->isEmpty()) {
            $typeBanner = $menuDefaultBannerCollection->where("id", 0)->first();
        } else {
            $typeBanner = $menuDefaultBannerCollection->where("id", $shopMenuId)->first();
        }
        $typeBannerImgUrl = "";

        if (
            $stinkyTofuProductDetail
            &&
            (empty($stinkyTofuProductStatusDetail) || ($stinkyTofuProductStatusDetail->status == 0))
        ) {
            //有臭豆腐
            switch ($sort) {
                case CameraModel::TV_POSITION_SORT_1:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.cdf", "");
                    break;
                case CameraModel::TV_POSITION_SORT_2:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.jrcAndDlj", "");
                    break;
                case CameraModel::TV_POSITION_SORT_3:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.zzbAndjlc", "");
                    break;
                case CameraModel::TV_POSITION_SORT_4:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.nrcAndDjp", "");
                    break;
                case CameraModel::TV_POSITION_SORT_5:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.ad5", "");
                    break;
                case CameraModel::TV_POSITION_SORT_6:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.ad6", "");
                    break;
            }
        } else {
            //没有臭豆腐
            switch ($sort) {
                case CameraModel::TV_POSITION_SORT_1:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.jrcAndDlj", "");
                    break;
                case CameraModel::TV_POSITION_SORT_2:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.zzbAndjlc", "");
                    break;
                case CameraModel::TV_POSITION_SORT_3:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.nrcAndDjp", "");
                    break;
                case CameraModel::TV_POSITION_SORT_4:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.yyyAndyrc", "");
                    break;
                case CameraModel::TV_POSITION_SORT_5:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.ad5", "");
                    break;
                case CameraModel::TV_POSITION_SORT_6:
                    $typeBannerImgUrl = attributeGet($typeBanner, "adv.ad6", "");
                    break;
            }
        }

        return [
            [
                "id"           => 0,
                "name"         => "默认图",
                "resourceType" => BannerCommonModel::RESOURCE_TYPE_IMG,
                "url"          => $typeBannerImgUrl,
                "type"         => $positionMap[$sort],
                "typeText"     => BannerEnum::TYPE_MAP[$positionMap[$sort]]
            ]
        ];
    }

    /**
     * 广告素材组合单个搜索
     * @param int $id
     * @param int $shopId
     * @return array
     */
    public static function bannerSearchById(int $id, int $shopId): array
    {
        $bannerInfo = BannerCommonModel::query()
                                       ->where('id', $id)
                                       ->where('type', BannerEnum::TYPE_TV_MATERIAL_1)
                                       ->where('status', BannerEnum::STATUS_ON)
                                       ->first();
        if ($bannerInfo) {
            $imgs      = json_decode($bannerInfo->imgs, true);
            $childIds  = array_unique(array_column($imgs, 'id'));
            $childList = [];
            if ($childIds) {
                $childList = self::getBannerProductById($shopId, $childIds);
            }
            return [
                'id'        => $bannerInfo->id,
                'name'      => $bannerInfo->name,
                'imgUrl'    => '',
                'childIds'  => $childIds,
                'childList' => $childList,
            ];
        }
        return [];
    }

    /**
     * 查询单品单菜配置图
     * @param int $shopId
     * @param array $childIds
     * @return array
     */
    public static function getBannerProductById(int $shopId, array $childIds): array
    {
        $shopInfo   = ShopService::getShopInfo($shopId);
        $shopMenuId = $shopInfo['menuId'] ?? 0;

        // 获取单菜 ID
        $bannerList = BannerCommonModel::query()
                                       ->whereIn("id", $childIds)
                                       ->where("type", BannerEnum::TYPE_TV_MATERIAL_2)
                                       ->where("status", BannerEnum::STATUS_ON)
                                       ->get()
                                       ->each(function ($v) {
                                           $v->imgArr = array_column(json_decode($v->imgs, true), null,
                                               'price');
                                       });

        $productList = Product::query()
                              ->where("menuId", $shopMenuId)
                              ->where("status", 0)
                              ->whereIn("id", $bannerList->pluck('productId'))
                              ->select(['id', 'price'])
                              ->get()
                              ->keyBy('id')->toArray();

        $returnList = [];
        foreach ($bannerList as $banner) {
            $returnList[] = [
                'id'           => $banner->id,
                'name'         => $banner->name,
                'resourceType' => $banner->resourceType,
                'url'          => (function ($banner, $productList) {
                    // 判断菜 是否存在
                    if (empty($productList[$banner->productId])) {
                        return $banner['imgArr']['-1']['img'] ?? '';
                    }
                    $imgArr = $banner['imgArr'];

                    // 判断价格是否存在
                    if (isset($imgArr[$productList[$banner->productId]['price']]['img']) && $imgArr[$productList[$banner->productId]['price']]['img']) {
                        return $imgArr[$productList[$banner->productId]['price']]['img'];
                    }
                    return $banner['imgArr']['-1']['img'] ?? '';
                })($banner, $productList),
                'type'         => $banner->type,
                'typeText'     => BannerEnum::TYPE_MAP[$banner->type]
            ];
        }
        return $returnList;
    }


    public static function getDeviceConfig(string $serialNo): array
    {
        $cameraInfo = DeviceService::getDeviceRow($serialNo);

        if (empty($cameraInfo)) {
            return [];
        }

        return [
            'sort'          => $cameraInfo->sort,
            'soundSwitch'   => $cameraInfo->soundSwitch,
            'queueSwitch'   => $cameraInfo->queueSwitch,
            'popPosition'   => (int)$cameraInfo->deviceName, // 叫号弹窗方向 1 左 2 右 , 复用 dn 字段
            'isQueueDevice' => (int)$cameraInfo->productKey, // 是否叫号主设备 , 复用 pk 字段
            'type'          => $cameraInfo->type,
            'typeText'      => CameraModel::DEVICE_TYPE_1_TYPE_TEXT[$cameraInfo->type] ?? "",
        ];

    }

    /**
     * 获取缓存数据
     *
     * @param string $serialNo
     * @return array
     */
    public static function getDeviceConfigCache(string $serialNo): array
    {
        return self::getDeviceConfig($serialNo);
    }

    /**
     * 获取设备详情
     *
     * @return Builder|Model|object|null
     */
    public static function getDeviceRow(string $serialNo)
    {
        return Cache::remember('device:getDeviceRow:cache:' . $serialNo, 86400, function () use ($serialNo) {
            return CameraModel::query()->where("serialNo", $serialNo)->first();
        });
    }

    /**
     * 删除商屏的缓存
     *
     * @return void
     */
    public static function forgetGetDeviceRowKey(string $serialNo)
    {
        Cache::forget('device:getDeviceRow:cache:' . $serialNo);
        Cache::forget('device:getDeviceMaterial:cache:' . $serialNo);
        DeviceCacheEnum::rmDeviceShopTvAdvCache($serialNo);
    }


    /**
     * 合成接口
     *
     * @param object $params
     * @return array|void
     * @throws BaseException
     */
    public static function mergeImageFace(object $params)
    {
        try {
            $config = \Kuafu\Recognizer\Util\Config::get();
            $config = new Config([
                "accessKeyId"     => attributeGet($config, "AccessKeyId", ""),
                "accessKeySecret" => attributeGet($config, "AccessKeySecret", ""),
            ]);
            // 访问的域名
            $config->endpoint = "facebody.cn-shanghai.aliyuncs.com";
            $client           = new Facebody($config);

            $file                         = fopen($params->image, 'rb');
            $stream                       = new Stream($file);
            $mergeImageFaceAdvanceRequest = new MergeImageFaceAdvanceRequest([
                "templateId"     => $params->templateId,
                "imageURLObject" => $stream
            ]);
            $runtime                      = new RuntimeOptions([]);
            $response                     = jsonToArr(
                Utils::toJSONString($client->mergeImageFaceAdvance($mergeImageFaceAdvanceRequest, $runtime)), []
            );

            $imageUrl = attributeGet($response, "body.Data.ImageURL", "");
            $ossUrl   = FileService::transferred(
                $imageUrl,
                vsprintf("%s/%s.%s", [
                    "mergeImageFace",
                    md5($imageUrl),
                    explode("?", File::extension($imageUrl))[0]
                ]),
                "mergeImageFace"
            );
            return [
                "imageUrl"  => $ossUrl,
                "requestId" => attributeGet($response, "body.RequestId", ""),
                "qrCode"    => WechatMiniProgramService::genImageFusion($ossUrl)
            ];
        } catch (Exception $exception) {
            if ($exception->getCode() == 'InvalidParameter') {
                handleException($exception, '未识别人像，请重新拍摄。');
            } else {
                handleException($exception, $exception->getMessage());
            }
        }
    }

    /**
     * 上传模板
     *
     * @param string $templateImg
     * @return array|void
     * @throws BaseException
     */
    public static function uploadMergeImageTemplate(string $templateImg)
    {
        try {
            $config = \Kuafu\Recognizer\Util\Config::get();
            $config = new Config([
                "accessKeyId"     => attributeGet($config, "AccessKeyId", ""),
                "accessKeySecret" => attributeGet($config, "AccessKeySecret", ""),
            ]);
            // 访问的域名
            $config->endpoint = "facebody.cn-shanghai.aliyuncs.com";
            $client           = new Facebody($config);
            //上传模板
            $file                         = fopen($templateImg, 'rb');
            $stream                       = new Stream($file);
            $addFaceImageTemplateaRequest = new AddFaceImageTemplateAdvanceRequest([
                "imageURLObject" => $stream
            ]);

            $runtime = new RuntimeOptions([]);
            $resp    = $client->addFaceImageTemplateAdvance($addFaceImageTemplateaRequest, $runtime);
            return [
                "templateId" => attributeGet(jsonToArr(Utils::toJSONString($resp->body), []), "Data.TemplateId", "")
            ];
        } catch (Exception $exception) {
            handleException($exception, $exception->getMessage());
        }
    }

    /**
     * @return array|void
     * @throws BaseException
     */
    public static function mergeImageTemplate()
    {
        try {
            return [
                "templateList" => ConfigService::getConfigDetail("mergeImage", "mergeImageTemplate", 0)["value"]
            ];
        } catch (Exception $exception) {
            handleException($exception, $exception->getMessage());
        }
    }
}
